package com.meta.nd.service.impl;

import java.util.List;

import com.meta.nd.service.DefaultWiredServiceImpl;
import com.meta.nd.service.WiredService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.meta.cache.NdCacheService;
import com.meta.common.ConstDef;
import com.meta.nd.service.ExecuteRuleService;
import com.meta.nd.vo.Nd;
import com.meta.nd.vo.Nv;
import com.meta.nd.vo.Rd;
import com.meta.rule.Rule;
import com.meta.rule.RuleRunContext;

import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import org.springframework.util.StringUtils;


@Slf4j
@Service
public class ExecuteRuleServiceImpl implements ExecuteRuleService {
	private static final WiredService defaultWiredService = new DefaultWiredServiceImpl();
	@Autowired
	NdCacheService ndCacheService;

private WiredService getWiredService(Nd nd){
	String wiredServiceClassName = nd.getServiceClassName();
	if (StringUtils.isEmpty(wiredServiceClassName) )
		return ExecuteRuleServiceImpl.defaultWiredService;
	else{
		try{
			return (WiredService)Class.forName(wiredServiceClassName).newInstance();
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e.getMessage(),e);
		} catch (InstantiationException e) {
			throw new RuntimeException(e.getMessage(),e);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e.getMessage(),e);
		}
	}
}

public void executeNvRule(String transactionType, Nv nv, RuleRunContext context ) {
	WiredService wiredService = this.getWiredService(nv.getNd());
	GroovyClassLoader loader= new GroovyClassLoader();
	this.executeRuleOnSingleNv(transactionType,ConstDef.RULE_EXECUTE_ORDER_TYPE_BEGIN,nv,context,loader);
	if (!context.isSuccess()) return;
	wiredService.doPreWiredMethod(transactionType,nv,context);
	if (!context.isSuccess()) return;
	this.executeRuleOnSingleNv(transactionType,ConstDef.RULE_EXECUTE_ORDER_TYPE_PRE,nv,context,loader);
	if (!context.isSuccess()) return;
	for ( String childName :  nv.getChildrenMap().keySet()) {
		List<Nv> childNvs = nv.getChildNvs(childName);
		for ( Nv childNv : childNvs ) {
			executeNvRule(transactionType,childNv,context);
			if (!context.isSuccess())
				return;
		}
	}
	this.executeRuleOnSingleNv(transactionType,ConstDef.RULE_EXECUTE_ORDER_TYPE_POST,nv,context,loader);
	if (!context.isSuccess()) return;
	wiredService.doPostWiredMethod(transactionType,nv,context);
	if (!context.isSuccess()) return;
	this.executeRuleOnSingleNv(transactionType,ConstDef.RULE_EXECUTE_ORDER_TYPE_END,nv,context,loader);
	if (!context.isSuccess()) return;
}


public void executeNvsRule(String transactionType, List<Nv> nvs, RuleRunContext context ){
	if ( nvs == null || nvs.size() == 0)
		return;
	WiredService wiredService = this.getWiredService(nvs.get(0).getNd());
	GroovyClassLoader loader= new GroovyClassLoader();
	for( Nv nv : nvs) {
		this.executeRuleOnSingleNv(transactionType, ConstDef.RULE_EXECUTE_ORDER_TYPE_BEGIN, nv, context, loader);
		if (!context.isSuccess()) return;
		wiredService.doPreWiredMethod(transactionType, nv, context);
		if (!context.isSuccess()) return;
		this.executeRuleOnSingleNv(transactionType, ConstDef.RULE_EXECUTE_ORDER_TYPE_PRE, nv, context, loader);
		if (!context.isSuccess()) return;
		for (String childName : nv.getChildrenMap().keySet()) {
			List<Nv> childNvs = nv.getChildNvs(childName);
			executeNvsRule(transactionType, childNvs, context);
			if ( !context.isSuccess())
				return;
		}
		this.executeRuleOnSingleNv(transactionType, ConstDef.RULE_EXECUTE_ORDER_TYPE_POST, nv, context, loader);
		if (!context.isSuccess()) return;
		wiredService.doPostWiredMethod(transactionType, nv, context);
		if (!context.isSuccess()) return;
		this.executeRuleOnSingleNv(transactionType, ConstDef.RULE_EXECUTE_ORDER_TYPE_END, nv, context, loader);
		if (!context.isSuccess()) return;
	}
}

private void executeRuleOnSingleNv(String transactionType, String orderType, Nv nv, RuleRunContext context, GroovyClassLoader loader){
	 Nd nd = nv.getNd();
	 List<Rd> rds = nd.getRds(transactionType,orderType);
	 for ( Rd rd : rds ){
		 Rule rule = this.ndCacheService.getRule(rd.getCode());
		 try {
			 Class groovyClass = rule.getGroovyClass();
			 if ( groovyClass == null ) {
				 groovyClass = loader.parseClass(rule.getRuleText());
				 rule.setGroovyClass(groovyClass);
			 }
			 GroovyObject groovyObject =(GroovyObject) groovyClass.newInstance();
			 groovyObject.invokeMethod("run", new Object[] {context,nv} );
		 }catch (Exception e ) {
			 context.setSuccess(false);
			 context.addMessage("ruleCode:"+ rule.getCode());
			 context.addMessage(e.getMessage());
			 log.error(e.getMessage(),e);
			 return;
		 }
	 }
}
	
public void executeNvRuleByOrderType(String transactionType, String orderType, Nv nv, RuleRunContext context ) {
		GroovyClassLoader loader= new GroovyClassLoader();
		this.executeRuleOnSingleNv(transactionType,ConstDef.RULE_EXECUTE_ORDER_TYPE_BEGIN,nv,context,loader);
		for ( String childName :  nv.getChildrenMap().keySet()) {
			List<Nv> childNvs = nv.getChildNvs(childName);
			for ( Nv childNv : childNvs ) {
				executeNvRuleByOrderType(transactionType,orderType,childNv,context);
				if (!context.isSuccess()) return;
			}
		}
	}
}
